
#include"util.h"
#include"log.h"
#include"matcher.h"
#include"online.h"
#include"room.h"
#include"session.h"
#include"db.h"



#define WWWROOT "./wwwroot/"
class gobang_server
{
    private:
        std::string _web_root=WWWROOT;
        users_tables _ut;
        online_manager _om;
        room_manager _rm;
        matcher _mm;
        session_manager _sm;
        wsserver_t _server;

    private:
        void http_resp(wsserver_t::connection_ptr &conn, bool result, 
            websocketpp::http::status_code::value code, const std::string &reason)
            {
                Json::Value resp;
                resp["result"]=result;
                resp["reason"]=reason.c_str();
                conn->set_status(code);
                std::string response;
                Json_util::serialize(resp,response);
                conn->set_body(response);
                conn->append_header("Content-Type", "application/json");
            return;
            }


        void reg( wsserver_t::connection_ptr conn)
        {
            
            websocketpp::http::parser::request req =conn->get_request();
            std::string req_body=req.get_body();
            Json::Value user_info;
           bool ret= Json_util::unserialize(req_body,user_info);
            if(ret==false)
            {
                ERR_Log("反序列化失败");
                http_resp(conn,false,websocketpp::http::status_code::bad_request,"反序列化错误");
                return;
            }
            //login_info["username"].isNull() || login_info["password"].isNull
            std::string password1=user_info["password"].asString();
            std::string username1=user_info["username"].asString();
            // ERR_Log("密码%s",password1.c_str());
            // ERR_Log("用户名%s",username1.c_str());
           
            // ERR_Log("密码：%ld",password1.size());
            // ERR_Log("用户名：%ld",username1.size());

            if(password1.size()==0||username1.size()==0)
            {
               ERR_Log("用户名或密码不能为空");
                http_resp(conn,false,websocketpp::http::status_code::bad_request,"password or username can not be null");
                return ;

            }
            bool ret2= _ut.insert(user_info);
            if(ret2==false)
            {
                ERR_Log("用户名重复了");
                http_resp(conn,false,websocketpp::http::status_code::bad_request,"用户名重复了");
                return;

            }
            http_resp(conn,true,websocketpp::http::status_code::ok,"注册成功");
            return;

        }

        void login(wsserver_t::connection_ptr conn)
        {

           
            websocketpp::http::parser::request req =conn->get_request();
            std::string req_body=req.get_body();
            Json::Value user_info;
           bool ret= Json_util::unserialize(req_body,user_info);
            if(ret==false)
            {
                ERR_Log("反序列化失败");
                http_resp(conn,false,websocketpp::http::status_code::bad_request,"反序列化错误");
                return ;
            }
            
            bool ret2= _ut.login(user_info);
            if(ret2==false)
            {
                ERR_Log("用户名或密码错误");
                http_resp(conn,false,websocketpp::http::status_code::bad_request,"用户名或密码错误");
                return ;

            }
            http_resp(conn,true,websocketpp::http::status_code::ok,"登录成功");
            uint64_t uid=user_info["id"].asUInt64();
            session_ptr ssp=_sm.create_session(uid,is_LOGIN);
            if(ssp.get()==nullptr)
            {
                ERR_Log("uid 为%ld的session创建失败",uid);
                http_resp(conn,false, websocketpp::http::status_code::internal_server_error,"服务器创建session失败");
                
            }
             _sm.set_session_expire_time(ssp->ssid(), SESSION_TIMEOUT);
            //4. 设置响应头部：Set-Cookie,将sessionid通过cookie返回
            std::string cookie_ssid = "SSID=" + std::to_string(ssp->ssid());
            conn->append_header("Set-Cookie", cookie_ssid);
            return http_resp(conn, true, websocketpp::http::status_code::ok , "登录成功");
        }
        
            
        bool get_cookie_val(std::string cookie_str, const std::string &key, std::string &val)
        {
            std::string sep = ": ";
            std::vector<std::string> v1;
            std::vector < std::string> v2;
            std::string sep2="=";
            string_util::split(cookie_str, sep, v1);
            for(auto e:v1)
            {

                string_util::split(e, sep2, v2);
                if (v2.size() != 2) { continue; }
                if (v2[0]==key)
                {
                    val = v2[1];
                    return true;
                }
            }
            return false;

        }
            
            
            
      

        void info(wsserver_t::connection_ptr conn)
        {
            //1.得到ssid
            //2.用ssid从_sm中得到session_spr
            //3.session_spr中得到uid
            //4.用uid从表中得到用户信息
            //5.发给用户
            // 6. 刷新session的过期时间
          
   
            std::string cookie_str = conn->get_request_header("Cookie");
            if (cookie_str.size() == 0)
            {
                http_resp(conn, false, websocketpp::http::status_code::bad_request, "获取Cookie失败请重新登录");
                return;
            }
            std::string ssid_str;
            bool ret = get_cookie_val(cookie_str, "SSID",ssid_str );
            if(ret==false)
            {
                http_resp(conn, false, websocketpp::http::status_code::bad_request, "获取Cookie ssid失败请重新登录");
                return;
            }
            session_ptr sp= _sm.get_session_by_ssid(std::stol(ssid_str));
            if(sp.get()==nullptr)
            {
                http_resp(conn, false, websocketpp::http::status_code::bad_request, "登录过期，请重新登录");
                return;
            }
            uint64_t uid=sp->get_user();
            Json::Value user_info;
            ret = _ut.select_by_id(uid, user_info);
            if(ret==false)
            {
                http_resp(conn, false, websocketpp::http::status_code::bad_request, "找不到用户信息，请重新登录");
                return;
            }
            std::string body;
            Json_util::serialize(user_info, body);
            conn->set_body(body);
            conn->append_header("Content-Type", "application/json");
            conn->set_status(websocketpp::http::status_code::ok);
            
            _sm.set_session_expire_time(sp->ssid(), SESSION_TIMEOUT);

           



        }

        void file_hadle(wsserver_t::connection_ptr conn)
        {
            websocketpp::http::parser::request req =conn->get_request();
            std::string url=req.get_uri();
            std::string resp_body;
      
           
            std::string real_path=_web_root+url;
            if (real_path.back() == '/') {
                real_path += "login.html";
            }
        
            
            bool ret=file_util::read(real_path,resp_body);

           
            if(ret==false)
            {
                 DBG_Log("发送uri为%s资源失败",real_path.c_str());
                std::string body;
                body += "<html>";
                body += "<head>";
                body += "<meta charset='UTF-8'/>";
                body += "</head>";
                body += "<body>";
                body += "<h1> Not Found </h1>";
                body += "</body>";
                conn->set_status(websocketpp::http::status_code::not_found);
                conn->set_body(body);
                return;
            }

            conn->set_status(websocketpp::http::status_code::ok);
            conn->set_body(resp_body);
            DBG_Log("发送uri为%s资源成功",real_path.c_str());

           


             
        }


    private:
        void http_callback( websocketpp::connection_hdl hdl) {
          

            wsserver_t::connection_ptr conn=_server.get_con_from_hdl(hdl);
            websocketpp::http::parser::request req =conn->get_request();
            std::string url=req.get_uri();
            std::string body=req.get_body();
            std::string method=req.get_method();
            
            // std::cout<<"url: "<<url<<std::endl;
            // std::cout<<"method: "<<method<<std::endl;
            // std::cout<<"body: "<<body<<std::endl;

           if(method=="POST"&&url=="/reg")
           {
                reg(conn);
           }
           else if(method=="POST"&&url=="/login")
           {
                login(conn);
           }
           else if(method=="GET"&&url=="/info")
           {
                info(conn);
           }
           else
           {
                
                file_hadle(conn);
           }
        
    }






    void ws_send(wsserver_t::connection_ptr conn, Json::Value body)
    {
        std::string resp_str;
        Json_util::serialize(body, resp_str);
        conn->send(resp_str);
    }

    session_ptr get_session_by_cookie(wsserver_t::connection_ptr conn,std::string s="hall_ready")
    {
        std::string req_header;

        req_header = conn->get_request_header("Cookie");
        // get_cookie_val(std::string cookie_str, const std::string &key, std::string &val)
        std::string key = "SSID";
        std::string ssid_str;
        get_cookie_val(req_header, key, ssid_str);
        Json::Value err_resp;
        if(ssid_str.size()==0)
        {
            err_resp["optype"] = s.c_str();
            err_resp["result"] = false;
            err_resp["reason"] = "找不到cookie,请重新登录";
            ERR_Log("找不到cookie,请重新登录");
            ws_send(conn, err_resp);
            return session_ptr();

        }
        session_ptr sp = _sm.get_session_by_ssid(stol(ssid_str));
        
        if (sp.get() == nullptr)
        {
            err_resp["optype"] = "s.c_str()";
            err_resp["result"] = false;
            err_resp["reason"] = "找不到session,请重新登录";
            ERR_Log("找不到session,请重新登录");
            ws_send(conn, err_resp);
            return session_ptr();
        }
        return sp;

    }
    













    //建立websocket请求

        void wsopen_game_hall(wsserver_t::connection_ptr conn)
        {   
            //游戏大厅长连接建立成功
            Json::Value resp_json;
            //1. 登录验证--判断当前客户端是否已经成功登录
            session_ptr ssp = get_session_by_cookie(conn);
            if (ssp.get() == nullptr) {
                 DBG_Log("ssp为空");
                return;
            }
            //2. 判断当前客户端是否是重复登录
            if (_om.is_in_game_hall(ssp->get_user()) || _om.is_in_game_room(ssp->get_user())) {
                resp_json["optype"] = "hall_ready";
                resp_json["reason"] = "玩家重复登录！";
                resp_json["result"] = false;
                DBG_Log("重复登陆");
                return ws_send(conn, resp_json);
            }
            //3. 将当前客户端以及连接加入到游戏大厅
            _om.enter_game_hall(ssp->get_user(), conn);
            //4. 给客户端响应游戏大厅连接建立成功
            resp_json["optype"] = "hall_ready";
            resp_json["result"] = true;
            ws_send(conn, resp_json);
            //5. 记得将session设置为永久存在
            _sm.set_session_expire_time(ssp->ssid(), SESSION_FOREVER);
            DBG_Log("添加用户:%ld到游戏大厅成功",ssp->get_user());
            return;
        }
         void wsopen_game_room(wsserver_t::connection_ptr conn)
        {
            
             Json::Value resp_json;
            //1. 登录验证--判断当前客户端是否已经成功登录
            session_ptr ssp = get_session_by_cookie(conn,"room_ready");
            //  DBG_Log("添加用户:%ld到游戏房间成功",ssp->get_user());
            room_ptr rp=_rm.get_room_by_uid(ssp->get_user());
            if (ssp.get() == nullptr) {
                 DBG_Log("ssp为空");
                return;
            }
            // DBG_Log("添加用户:%ld到游戏房间成功",ssp->get_user());
            //2. 判断当前客户端是否是重复登录
            // std::cout<<_om.is_in_game_hall(ssp->get_user())<<std::endl;
            // std::cout<<_om.is_in_game_room(ssp->get_user())<<std::endl;
            if (_om.is_in_game_hall(ssp->get_user()) || _om.is_in_game_room(ssp->get_user())) {
                resp_json["optype"] = "room_ready";
                resp_json["reason"] = "玩家重复登录！";
                resp_json["result"] = false;
                DBG_Log("重复登陆");
                return ws_send(conn, resp_json);
            }
            // DBG_Log("添加用户:%ld到游戏房间成功",ssp->get_user());
            //3. 将当前客户端以及连接加入到游戏房间
            _om.enter_game_room(ssp->get_user(), conn);
             //4. 给客户端响应房间连接建立成功
            resp_json["optype"] = "room_ready";
            resp_json["result"] = true;
            resp_json["room_id"] = (Json::UInt64)rp->id();
            resp_json["uid"] = (Json::UInt64)ssp->get_user();
            resp_json["white_id"] = (Json::UInt64)rp->get_white_user();
            resp_json["black_id"] = (Json::UInt64)rp->get_black_user();
            ws_send(conn, resp_json);
            // DBG_Log("添加用户:%ld到游戏房间成功",ssp->get_user());
            //5. 记得将session设置为永久存在
            _sm.set_session_expire_time(ssp->ssid(), SESSION_FOREVER);
            DBG_Log("添加用户:%ld到游戏房间成功",ssp->get_user());
            return;
        }
        void wsopen_callback( websocketpp::connection_hdl hdl) {
            std::cout<<"websock握手成功"<<std::endl;
            wsserver_t::connection_ptr conn = _server.get_con_from_hdl(hdl);
            websocketpp::http::parser::request req = conn->get_request();
            std::string uri = req.get_uri();
            if(uri=="/hall")
            {
                return wsopen_game_hall(conn);
            }
            else if(uri=="/room")
            {
                return wsopen_game_room(conn);
            }
            else
            {
                ERR_Log("请求路径出错");
                Json::Value resp_json;
                resp_json["optype"] = "hall_ready";
                resp_json["reason"] = "未知请求！";
                resp_json["result"] = false;
                
                return ws_send(conn, resp_json);

            }

        
        }






















        void wsclose_game_hall(wsserver_t::connection_ptr conn)
        {
             session_ptr ssp = get_session_by_cookie(conn);
             if(ssp.get()==nullptr)
             {
                return;
             }
             _om.exit_game_hall(ssp->get_user());
              _sm.set_session_expire_time(ssp->ssid(), SESSION_TIMEOUT);
              DBG_Log("用户:%ld退出游戏大厅成功",ssp->get_user());
              //INF_Log("session设置为临时的");
              return;

        }
         void wsclose_game_room(wsserver_t::connection_ptr conn)
        {
            session_ptr ssp = get_session_by_cookie(conn,"room_ready");
            if(ssp.get()==nullptr)
            {
                return;
            }
             
              _om.exit_game_room(ssp->get_user());
               _rm.remove_room_user(ssp->get_user());
              _sm.set_session_expire_time(ssp->ssid(), SESSION_TIMEOUT);
              DBG_Log("用户:%ld退出游戏房间成功",ssp->get_user());
             // INF_Log("session设置为临时的");
              return;



        }
        void wsclose_callback(websocketpp::connection_hdl hdl) {
            std::cout<<"websocket握手失败"<<std::endl;
            wsserver_t::connection_ptr conn = _server.get_con_from_hdl(hdl);
            websocketpp::http::parser::request req = conn->get_request();
            std::string uri = req.get_uri();
            if(uri=="/hall")
            {
                return wsclose_game_hall(conn);
            }
            else if(uri=="/room")
            {
                return wsclose_game_room(conn);
            }
            else
            {
               
                ERR_Log("请求路径出错");
                return;

            }

        }

        void wsmsg_game_room(wsserver_t::connection_ptr conn, wsserver_t::message_ptr msg) {
            //1.得到uid
             Json::Value resp_json;
            session_ptr ssp=get_session_by_cookie(conn);
             if(ssp.get()==nullptr)
            {
                ERR_Log("获取session出错");
                return;
            }
            room_ptr rp=_rm.get_room_by_uid(ssp->get_user());
            Json::Value req_json;
            std::string req_str;
            req_str=msg->get_payload();
            bool ret=Json_util::unserialize(req_str,req_json);
            if(!ret)
            {
                resp_json["optype"] = "unknow";
                resp_json["result"]=false;
                resp_json["reason"]="反序列化失败";
                ws_send(conn,resp_json);
                return;

            }
            //4. 通过房间模块进行消息请求的处理
            return rp->handle_request(req_json);



        }
        void wsmsg_game_hall(wsserver_t::connection_ptr conn, wsserver_t::message_ptr msg) {

            Json::Value resp_json;
             //1.得到uid
            session_ptr ssp=get_session_by_cookie(conn);
            if(ssp.get()==nullptr)
            {
                ERR_Log("获取session出错");
                return;
            }
            //2.得到数据
            Json::Value req_json;
            std::string req_str;
            req_str=msg->get_payload();
            bool ret=Json_util::unserialize(req_str,req_json);
            if(!ret)
            {
                ERR_Log("序列化出错");
                resp_json["result"]=false;
                resp_json["reason"]="反序列化失败";
                ws_send(conn,resp_json);
                return;

            }
             if (!req_json["optype"].isNull() && req_json["optype"].asString()=="match_start")
            {
                _mm.add(ssp->get_user());
                resp_json["optype"] = "match_start";
                resp_json["result"] = true;
                return ws_send(conn, resp_json);
            }
            else if (!req_json["optype"].isNull() && req_json["optype"].asString() == "match_stop")
            {
                //  停止对战匹配：通过匹配模块，将用户从匹配队列中移除
                _mm.del(ssp->get_user());
                resp_json["optype"] = "match_stop";
                resp_json["result"] = true;
                return ws_send(conn, resp_json);
            }
            resp_json["optype"] = "unknow";
            resp_json["reason"] = "请求类型未知";
            resp_json["result"] = false;
            return ws_send(conn, resp_json);



        }
        void wsmsg_callback(websocketpp::connection_hdl hdl, wsserver_t::message_ptr msg) {
            wsserver_t::connection_ptr conn=_server.get_con_from_hdl(hdl);
            std::string message;
            message=msg->get_payload();
             websocketpp::http::parser::request req = conn->get_request();
            std::string uri = req.get_uri();
            INF_Log("服务器收到消息%s,url为%s",message.c_str(),uri.c_str());
            
            if (uri == "/hall") {
                //建立了游戏大厅的长连接
                return wsmsg_game_hall(conn, msg);
            }else if (uri == "/room") {
                //建立了游戏房间的长连接
                return wsmsg_game_room(conn, msg);
            }
        }

        
    public:
        gobang_server():  _ut(),_om(),_rm(&_ut,&_om),_mm(&_rm,&_ut,&_om),_sm(&_server)
        {

        //2. 设置日志等级
        _server.set_access_channels(websocketpp::log::alevel::none);
    
        //3. 初始化asio调度器
        _server.init_asio();
       
        
        //4. 设置回调函数
        _server.set_http_handler(std::bind(&gobang_server:: http_callback,this,std::placeholders::_1));
        _server.set_open_handler(std::bind(&gobang_server::wsopen_callback,this,std::placeholders::_1));
        _server.set_close_handler(std::bind(&gobang_server::wsclose_callback,this,std::placeholders::_1));
        _server.set_message_handler(std::bind(&gobang_server::wsmsg_callback,this,std::placeholders::_1,std::placeholders::_2));    
        //5. 设置监听端口
        _server.set_reuse_addr(true);
      

    }
    void start(int port=8888)
    {
        
        _server.listen(port);
        //6. 开始获取新连接
        _server.start_accept();
        //7. 启动服务器
        _server.run();
    }


};